import time

import numpy as np
import pandas as pd
import matplotlib.cm as cm
import matplotlib.colors as mcolors

from module.static_module.parent.view import AdditionWindowView
from tools.view import *
from tools.framework import *
from conf import conf


class ComparisonChartView(AdditionWindowView):
    def __init__(self, super_model):
        super().__init__(super_model)
        self.sector_index_visual = None
        self.i = 0
        self.win_draw_num = super_model.draw_num
        self.category_ls = None
        self.ori_draw_data = None
        self.sector_index_draw_data = None

    def create_layout_init(self):
        # 绘制标签、内容组件
        # 配置参数
        mk_data_file_ls = self.super_model.mk_data_file_ls  # 行情文件
        symbol_name_ls = self.super_model.symbol_name_ls  # 标的名称

        label_config = [
            [LabelMember.MkDataFile, WidgetCategory.Entry, mk_data_file_ls, [0]],
            [LabelMember.SymbolName, WidgetCategory.Entry, symbol_name_ls, [0]]
        ]
        button_config = [
            [ButtonMember.Draw, WidgetCategory.Button, None]
        ]
        self.auto_layout = AutoLayout(self.frame, label_config, button_config)
        self.auto_layout.button_command(self)

    def draw_dynamic_graph(self):
        self.ori_draw_data = self.super_model.draw_data.copy()
        # 生成tk窗口
        root = tk.Toplevel()
        root.title("DDQuant 对比图")
        # 计算canvas布局，并生成canvas
        is_axis_y = True
        total_x_px = conf.AdditionModule.visualization_x_px.value + conf.AdditionModule.axis_y_px.value if is_axis_y \
            else conf.AdditionModule.visualization_x_px.value

        sector_index_y_px = conf.AdditionModule.visualization_x_px.value // 1.8

        root.geometry(f"{total_x_px+6}x{int(sector_index_y_px+conf.AdditionModule.axis_x_px.value+20)}")

        sector_index_frame = tk.Frame(root, borderwidth=1, relief='solid')
        sector_index_canvas = tk.Canvas(sector_index_frame, width=total_x_px, height=sector_index_y_px+conf.AdditionModule.axis_x_px.value)
        sector_index_canvas.pack(fill='both', expand=True)
        sector_index_frame.grid(row=0, column=0, sticky="nsew")

        # 初始化相应sub visual，并将canvas分配
        self.sector_index_visual = SubVisual(self)
        self.sector_index_visual.gen_layout(sector_index_y_px, True, is_axis_y)
        self.sector_index_visual.set_canvas(sector_index_canvas, conf.AdditionModule.color_ls.value, 2)

        self.category_ls = list(self.ori_draw_data.keys())
        # 配置颜色
        N = len(self.category_ls)
        if len(conf.AdditionModule.color_ls.value) < N:
            # 使用matplotlib的colormap来生成颜色
            colormap = cm.viridis  # 或者使用 'nipy_spectral', 'hsv', 'coolwarm' 等
            colors_rgba = [colormap(i) for i in np.linspace(0, 1, N)]

            tk_hex_colors = ["#{:02x}{:02x}{:02x}".format(int(rgba[0]*255), int(rgba[1]*255), int(rgba[2]*255)) for rgba in colors_rgba]
        else:
            tk_hex_colors = conf.AdditionModule.color_ls.value
        self.sector_index_visual.color_ls = tk_hex_colors

        self.update()

    def update(self):
        # 分拆数据为不同帧对应数据，设置数据并渲染

        # 生成px数据
        if self.i < self.win_draw_num * len(self.category_ls):
            category = self.category_ls[self.i // self.win_draw_num]
            sector_number = self.i // self.win_draw_num
            index_number = self.i % self.win_draw_num
            if index_number == 0:
                time.sleep(1)
            if self.sector_index_draw_data is None:
                self.sector_index_draw_data = pd.DataFrame(np.nan, index=self.ori_draw_data[category].index, columns=self.category_ls)
            else:
                pass
            # 组合出将要绘制的数据
            self.sector_index_draw_data.iloc[index_number, sector_number] = self.ori_draw_data[category]["close"].iloc[index_number]
            self.sector_index_visual.gen_scale_data(self.sector_index_draw_data)
            self.sector_index_visual.gen_px_data()
        else:
            return
        # 清空canvas内容
        self.sector_index_visual.del_canvas()
        # 绘制坐标轴
        self.sector_index_visual.draw_axis()
        # 绘制y轴垂直线
        self.sector_index_visual.draw_y_line()
        # 绘制元素
        self.sector_index_visual.draw_ele()
        # 绘制图例
        self.sector_index_visual.draw_legend()
        if self.i <= 1:
            self.root.after(3000, self.update)
        else:
            self.root.after(conf.AdditionModule.time_interval.value, self.update)
        pass

        self.i += 1


class SubVisual:
    def __init__(self, super_view):
        self.super_view = super_view
        self.draw_data = None
        self.draw_scale_data = None
        self.draw_px_data = None
        self.axis_x = False
        self.axis_y = False
        self.legend = True
        self.visualization_x = conf.AdditionModule.visualization_x_px.value
        self.visualization_y = None
        self.ele_size = []
        self.win_size = []
        self.max_min_ls = []
        self.canvas = None
        self.color_ls = None
        self.lines_size = None
        self.y_axis_px_ls = None

    def gen_layout(self, visualization_y_px, draw_axis_x: bool, draw_axis_y: bool):
        self.visualization_y = visualization_y_px
        self.axis_x = draw_axis_x
        self.axis_y = draw_axis_y
        if self.axis_y:
            self.win_size.append(self.visualization_x + conf.AdditionModule.axis_y_px.value)
        else:
            self.win_size.append(self.visualization_x)
        if self.axis_x:
            self.win_size.append(self.visualization_y + conf.AdditionModule.axis_x_px.value)
        else:
            self.win_size.append(self.visualization_y)

    def set_canvas(self, canvas, color_ls, lines_size):
        self.canvas = canvas
        self.color_ls = color_ls
        self.lines_size = lines_size

    def gen_px_data(self):
        self.max_min_ls = [self.draw_scale_data.max().max()*1.015, self.draw_scale_data.min().min()*0.985]
        distance = self.max_min_ls[0] - self.max_min_ls[1]
        temp_df = self.visualization_y - (self.draw_scale_data - self.max_min_ls[1]) / distance * self.visualization_y
        self.draw_px_data = temp_df

    def gen_scale_data(self, draw_data):
        self.draw_data = draw_data
        if isinstance(draw_data, pd.Series):
            self.draw_data = pd.DataFrame(draw_data)
        self.draw_scale_data = self.draw_data.copy()

        def divide_by_first_valid(series):
            # 使用dropna确保我们得到的是非NaN的第一个值
            first_valid = series.iloc[0]
            # 除以第一个非NaN值
            return series / first_valid

        self.draw_scale_data = self.draw_scale_data.apply(divide_by_first_valid)
        self.draw_scale_data.round(2)

    def draw_ele(self):
        # 绘制line
        k_cont_width = conf.AdditionModule.k_px.value
        k_x = 0
        line_points = []
        # 剔除可能的buy和sell字段
        all_field = self.draw_px_data.columns.tolist()
        real_field = []
        for field in all_field:
            if field not in ["buy", "sell"]:
                real_field.append(field)

        real_draw_px_data = self.draw_px_data[real_field]
        for j, (index, o) in enumerate(real_draw_px_data.iterrows()):
            show_notes = []
            x = k_x + k_cont_width
            y = tuple(o.tolist())
            line_points.append((x, y))
            k_x += k_cont_width

            if len(line_points) >= 2:
                x1 = line_points[-2][0]
                x2 = line_points[-1][0]
                for i, y_f in enumerate(y):
                    y1 = line_points[-2][1][i]
                    y2 = line_points[-1][1][i]
                    category = o.index[i]

                    text_value = round(self.draw_data.iloc[j - 1][category], 2)
                    text_value_str = str(
                        text_value) + f" - {category}"
                    if (not np.isnan(y1) and np.isnan(y2)) and category not in show_notes:
                        # 获取到了nan值，则展示备注信息

                        self.canvas.create_text(x1 + int(conf.AdditionModule.axis_y_px.value / 2), y1,
                                                text=text_value_str, font=conf.KChart.font.value, fill=self.color_ls[i])
                        # 绘制小圆点
                        radius = 2
                        self.canvas.create_oval(x1 - radius, y1 - radius, x1 + radius, y1 + radius,
                                                fill=self.color_ls[i])
                        # 同时标记本板块完工
                        show_notes.append(category)
                        pass
                    elif np.isnan(y1) and np.isnan(y2) and category in show_notes:
                        pass
                    elif j == len(self.draw_px_data)-1 and not np.isnan(self.draw_data.loc[index, category]):
                        self.canvas.create_line(x1, y1, x2, y2, width=self.lines_size, fill=self.color_ls[i])
                        # 获取到了nan值，则展示备注信息
                        self.canvas.create_text(x2 + int(conf.AdditionModule.axis_y_px.value / 2), y2,
                                                text=text_value_str, font=conf.KChart.font.value, fill=self.color_ls[i])
                        # 绘制小圆点
                        radius = 2
                        self.canvas.create_oval(x2 - radius, y2 - radius, x2 + radius, y2 + radius, fill=self.color_ls[i])
                        # 同时标记本板块完工
                        show_notes.append(category)
                    else:
                        self.canvas.create_line(x1, y1, x2, y2, width=self.lines_size, fill=self.color_ls[i])
        pass

    def draw_y_line(self):
        # 补充绘制y轴线
        x1 = 0
        x2 = conf.AdditionModule.visualization_x_px.value
        for y_axis_px in self.y_axis_px_ls:
            y1 = y2 = y_axis_px[1]
            self.canvas.create_line(x1, y1, x2, y2, width=1, fill="#D3D3D3")

    def draw_axis(self):
        if self.axis_x:
            k_px = conf.AdditionModule.k_px.value
            k_num_interval = conf.AdditionModule.axis_x_interval.value // k_px
            # 获取绘制点坐标及其数值
            draw_px_ls = []
            draw_text_ls = []
            draw_num = int(len(self.draw_data) // k_num_interval)+1  # 获取最多绘制数量
            for i in range(draw_num):
                if i == draw_num - 1:
                    continue  # 最后一个不绘制，为了美观
                # 绘制文本的坐标
                text_px_x = (i + 1) * k_num_interval * k_px - k_px / 2
                text_px_y = conf.AdditionModule.axis_x_px.value / 2
                draw_px_ls.append([text_px_x, text_px_y])
                # 绘制文本的数值，此时记录datetime类型的时间数据
                text_dt = self.draw_data.iloc[int(i*k_num_interval)].name  # 定位到该位置对应的k线数据并获取相应series的name
                draw_text_ls.append(text_dt)

            # 处理draw_text_ls，判断k线的级别
            interval_ts_ls = []
            index_ls = self.draw_data.index.tolist()
            for i, o in enumerate(index_ls):
                if i == 0:
                    continue
                interval_ts_ls.append(index_ls[i].timestamp() - index_ls[i - 1].timestamp())

            interval_ts_dc = {}
            for i, o in enumerate(interval_ts_ls):
                if o in interval_ts_dc.keys():
                    interval_ts_dc[o] += 1
                else:
                    interval_ts_dc[o] = 1

            key_ls = list(interval_ts_dc.keys())
            val_ls = list(interval_ts_dc.values())
            if val_ls:
                arg_max = np.argmax(val_ls)
                interval_ts = key_ls[arg_max]
                if interval_ts < 60:  # 如果小于一分钟
                    # 展示 月-日 时：分
                    for i, o in enumerate(draw_text_ls):
                        draw_text_ls[i] = o.strftime("%d %H:%M:%S")

                elif interval_ts < 60 * 60 * 24:  # 如果小于一天
                    # 展示 月-日 时：分
                    for i, o in enumerate(draw_text_ls):
                        draw_text_ls[i] = o.strftime("%m-%d %H:%M")
                else:
                    # 展示 年-月-日
                    for i, o in enumerate(draw_text_ls):
                        draw_text_ls[i] = o.strftime("%Y-%m-%d")
                # 画到图标上
                for i, o in enumerate(draw_text_ls):
                    text_x = draw_px_ls[i][0]
                    text_y = draw_px_ls[i][1]

                    self.canvas.create_text(text_x, text_y+self.visualization_y, text=o, font=conf.KChart.font.value, fill="black")
                    # 绘制|表示刻度
                    self.canvas.create_text(text_x, 0+self.visualization_y, text='.', font=conf.KChart.font.value, fill="black")
            else:
                pass
            pass
        if self.axis_y:

            y_axis_text_ls = []
            self.y_axis_px_ls = []

            # 最大绘制y坐标文本数量
            y_text_draw_num = int(self.visualization_y // conf.AdditionModule.axis_y_interval.value)
            # 确定0值
            if self.max_min_ls[0] > 0 > self.max_min_ls[1]:
                y_axis_text_ls.append("0")
                px_0 = value2px(0, self.max_min_ls, self.visualization_y)
                self.y_axis_px_ls.append([conf.AdditionModule.axis_y_px.value // 2, px_0])
                px_move = px_0
                while True:
                    px_move -= conf.AdditionModule.axis_y_interval.value
                    if px_move < 0:
                        break
                    y_axis_text_ls.append(str(px2value(px_move, self.max_min_ls, self.visualization_y)))
                    self.y_axis_px_ls.append([conf.AdditionModule.axis_y_px.value // 2, px_move])
                px_move = px_0
                while True:
                    px_move += conf.AdditionModule.axis_y_interval.value
                    if px_move > self.visualization_y:
                        break
                    y_axis_text_ls.append(str(px2value(px_move, self.max_min_ls, self.visualization_y)))
                    self.y_axis_px_ls.append([conf.AdditionModule.axis_y_px.value // 2, px_move])
            else:
                for i in range(y_text_draw_num):
                    px_move = (i + 1) * conf.AdditionModule.axis_y_interval.value
                    value = px2value(px_move, self.max_min_ls, self.visualization_y)
                    if np.isnan(value):
                        y_axis_text_ls.append('')
                    else:
                        y_axis_text_ls.append(str(value))
                    self.y_axis_px_ls.append([conf.AdditionModule.axis_y_px.value // 2, px_move])

            for i, text_o in enumerate(y_axis_text_ls):
                text = text_o
                text_x = self.y_axis_px_ls[i][0]
                text_y = self.y_axis_px_ls[i][1]
                self.canvas.create_text(text_x + self.visualization_x, text_y, text=text, font=conf.KChart.font.value, fill="black")
                # 绘制一根-表示刻度线
                self.canvas.create_text(0 + self.visualization_x, text_y, text='-', font=conf.KChart.font.value, fill="black")
            pass
        pass

    def draw_legend(self):
        # 剔除可能的buy和sell字段
        all_field = self.draw_px_data.columns.tolist()
        real_field = []
        for field in all_field:
            if field not in ["buy", "sell"]:
                real_field.append(field)
        if self.legend:
            # 生成框的坐标
            high = 18 * len(real_field)
            # text_cont_ax = [self.win_size[0]-100, 20, self.win_size[0]-20, 20+high]
            text_cont_ax = [20, 20, 120, 20 + high]
            # 创建文本框
            # text_cont_ax[0], text_cont_ax[1], text_cont_ax[2], text_cont_ax[3]
            self.canvas.create_rectangle(text_cont_ax[0], text_cont_ax[1], text_cont_ax[2], text_cont_ax[3],
                                         width=1,
                                         fill="white")
            # 框内写图例
            # ele_type类型： bar：绘制k线 volume：绘制交易量柱 line：绘制线条 point：绘制点 tank：绘制箱体
            # line_byk：绘制线条 point_byk：绘制点  // 区别在于byk以k线最大最小值为准，否则以对应指标最大最小值为准
            for i, o in enumerate(real_field):
                shape = '--'
                color = self.color_ls[i]
                self.canvas.create_text(text_cont_ax[0] + text_cont_ax[2] / 2 - 10,
                                        text_cont_ax[1] + 9 + i * 18,
                                        text=o + ':' + shape, font=conf.KChart.font.value, fill=color)
        pass

    def del_canvas(self):
        # 清空canvas
        self.canvas.delete("all")
